#ifndef _CVIRTUALTERM_H_
#define _CVIRTUALTERM_H_

#include <buola/utility/ccursorpos.h>
#include <boost/circular_buffer.hpp>
#include <buola/utility/usignal.h>
#include <buola/io/cfdstream.h>

namespace buola {

#define TERMATTR_BOLDBIT            0x0080
#define TERMATTR_BLINKBIT           0x0008
#define TERMATTR_REVERSEBIT         0x4000
#define TERMATTR_FOREMASK           0x00f0
#define TERMATTR_BACKMASK           0x000f

#define TERMATTR_DEFAULT            ((uint16_t)0x70)
#define TERMATTR_INVISIBLE          ((uint16_t)0)
#define TERMATTR_ISBOLD(a)          ((a)&TERMATTR_BOLDBIT)
#define TERMATTR_SETBOLD(a)         ((a)|=TERMATTR_BOLDBIT)
#define TERMATTR_UNSETBOLD(a)       ((a)&=~TERMATTR_BOLDBIT)
#define TERMATTR_ISBLINK(a)         ((a)&TERMATTR_BLINKBIT)
#define TERMATTR_SETBLINK(a)        ((a)|=TERMATTR_BLINKBIT)
#define TERMATTR_UNSETBLINK(a)      ((a)&=~TERMATTR_BLINKBIT)
#define TERMATTR_ISREVERSE(a)       ((a)&TERMATTR_REVERSEBIT)
#define TERMATTR_SETREVERSE(a)      ((a)|=TERMATTR_REVERSEBIT)
#define TERMATTR_UNSETREVERSE(a)    ((a)&=~TERMATTR_REVERSEBIT)
#define TERMATTR_SETFORECOLOR(a,c)  ((a)=((a)&~0x0070)|((c)<<4))
#define TERMATTR_SETBACKCOLOR(a,c)  ((a)=((a)&~0x0007)|(c))
#define TERMATTR_GETFORECOLOR(a)    (((a)&TERMATTR_FOREMASK)>>4)
#define TERMATTR_GETBACKCOLOR(a)    ((a)&TERMATTR_BACKMASK)

class CVirtualTerm : public io::DStreamActivity
{
    enum EMode
    {
        MODE_NONE=0,
        MODE_KEYPAD_APP=(1<<0),
        MODE_CURSOR_APP=(1<<1),
        MODE_ALTERNATE=(1<<2),
        MODE_AUTOWRAP=(1<<3),
        MODE_LINEFEED=(1<<4),
        MODE_ORIGIN=(1<<5),
        MODE_INSERT=(1<<6),
        MODE_X10_MOUSE=(1<<7),
        MODE_VT200_MOUSE=(1<<8),
        MODE_VT200_HIGHLIGHT_MOUSE=(1<<9),
        MODE_BTN_EVENT_MOUSE=(1<<10),
        MODE_ANY_EVENT_MOUSE=(1<<11),
        MODE_WANTS_MOUSE_EVENTS=(MODE_X10_MOUSE|MODE_VT200_MOUSE|
                                MODE_VT200_HIGHLIGHT_MOUSE|
                                MODE_BTN_EVENT_MOUSE|MODE_ANY_EVENT_MOUSE),
        MODE_FOCUS_EVENT_MOUSE=(1<<12),
        MODE_SHOW_CURSOR=(1<<13),
        MODE_BLINK_CURSOR=(1<<14),
        MODE_NETHACK=(1<<15)
    };

    DECLARE_FRIEND_ENUM_BITOPS(EMode)

public:
    struct SCell
    {
        wchar_t mC;
        uint16_t mAttr;
    };

    struct SLine : public UShared
    {
        std::vector<SCell> mCells;

        bool mCmdStart;
    };

    typedef intrusive_ptr<SLine> PLine;
    typedef boost::circular_buffer<PLine> TLineBuffer;
    typedef TLineBuffer::iterator TLineIter;

    CVirtualTerm(int pR,int pC);
    ~CVirtualTerm();

    void OnStreamCanRead(io::CFDStream *pStream) override;
    void ForkPty(const std::string &pCmd);
    void Disconnect();
    void Inject(const char *pBuf,int pSize);
    void Write(const char *pBuf,int pSize);
    void Write(const std::string &pStr);
    void Resize(int pR,int pC);
    bool ProcessKeyEvent(const gui::CKeyEvent &pE);
    void ProcessMouseEvent(const CCursorPos &pPos,const gui::CMouseEvent &pE);
    bool WantsMouseEvents()   { return mMode&MODE_WANTS_MOUSE_EVENTS;   }

    std::wstring GetTitle()  {   return (mMode&MODE_NETHACK)?(L"@ "+mTitle):mTitle;  }
    bool IsExited()         {   return mExited; }

    bool ShouldShowCursor()      {   return mMode&MODE_SHOW_CURSOR;  }

    CCursorPos GetCursor()        {   return mCursor; }
    CCursorPos GetSize()          {   return mSize;   }

    int GetTotalRows()      {   return mLines.size();   }

    PLine GetLine(int r)        {   return mLines[r+mLines.size()-mSize.r];     }

private:
    void WriteCursorKey(const char *pCode,EKeyMod pMod);
    void WriteModKey(const char *pPrefix,const char *pSuffix,EKeyMod pMod,bool pAddOne);
    
    SLine *NewEmptyLine();
    void SetCursorR(int pR);
    void SetCursorC(int pC);
    void CursorDown();
    void CursorUp();
    void NewEscapeSequence();
    void CancelEscapeSequence();
    void InterpretCSISequence();
    void InterpretXTermSequence();
    void InterpretEscapeSequence();
    void ProcessControlChar(char pC);
    void PutNormalChar(wchar_t pC);
    void PutGraphmodeChar(wchar_t pC);

    void SwitchAlternate();
    void ResizeRows(int pR);
    void ResizeCols(int pC);

    void DoDECALN();

    void DirtyLines(int pFrom,int pTo);
    void DirtyLine(int pLine)   {   DirtyLines(pLine,pLine);    }
    
public:
    USignal<void()> sDirtyAll;
    USignal<void(int,int)> sDirty;
    USignal<void()> sTitleChanged;
    USignal<void()> sExited;
    USignal<void()> sSizeChanged; //both terminal and back buffer

private:
    bool mSomeDirty;
    int mFirstDirty;
    int mLastDirty;

    std::wstring mTitle;
    bool mExited;

    TLineBuffer mLines;
    CCursorPos mSize;
    CCursorPos mCursor;
    CCursorPos mOrigin;

    uint16_t mCurAttr;

    pid_t mChildPID;

    bool mEscaped;
    bool mGraphMode;
    bool mUTF8;

    int mSentMouseButton;
    CCursorPos mSentMousePos;

    int mScrollTop,mScrollBottom;
//    int mSavedX,mSavedY;
    std::string mEscBuf;
    std::string mUTF8Buf;

    io::PFDStream mPty;

    EMode mMode;

    std::vector<int> mTabStops;

    struct
    {
        TLineBuffer mLines;
        CCursorPos mCursor;
        CCursorPos mSize;
        int mScrollTop;
        int mScrollBottom;
    } mAlternate;

    struct
    {
        CCursorPos mCursor;
        uint16_t mCurAttr;
    } mSaved;
};

/*namespace buola*/ }

#endif
